package code;

import java.io.EOFException; 
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream; 
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;

import result.ClassRequest;
import result.Result;

import comm.ConnectionType;


public class ClassCodeServer {

	public static int DEFAULTCLASSPORT = 10000;
	private int masterClassesPort = DEFAULTCLASSPORT;

	private ClassSolver classSolver = new ClassSolver();

	private HashMap<ClassRequest, byte[]> classes = new HashMap<ClassRequest, byte[]>();

	public void execute() {
		ReceiveConnectionsForClassesThread receiveConnectionsForClassesThread = new ReceiveConnectionsForClassesThread();
		receiveConnectionsForClassesThread.start();
		
		System.out.println("Class Code Server started... ");
		System.out.println("Version: April 1 - 2011");
	}

	public static void main(String[] args) {
		new ClassCodeServer().execute();
	}

	class ReceiveConnectionsForClassesThread extends Thread {

		public ReceiveConnectionsForClassesThread() {

		}

		public void run() {
			ServerSocket serverSocket = null;
			ObjectOutputStream out = null;
			ObjectInputStream in = null;
			try {
				serverSocket =  new ServerSocket(masterClassesPort);
				System.out.println("Server:"+serverSocket);
				while (true) {
					try {
						Socket socket = serverSocket.accept();
						System.out.println("socket"+socket);
						ConnectionType type;
						out = new ObjectOutputStream( (socket.getOutputStream()));

						in = new ObjectInputStream((socket.getInputStream()));


						Object input = in.readObject();
						if(!(input instanceof ConnectionType))
							System.out.println("Not what i was expecting...");
						else{
							type = (ConnectionType) input;

							switch (type) {
							case CLASS_SOLVER:

								int problemNumber = (Integer) in.readObject();
								int version = (Integer) in.readObject();
								System.out.println("Got new connection:" + type
										+ "(" + problemNumber + ", " + version
										+ ")");

								classSolver.add(problemNumber, version, out, in);
								
								System.out.println("New class solver connection "
										+ socket);
								break;
							case CLASS_REQUESTER:
								new RequesterThread(socket,in, out).start();
								System.out.println("New class requester connection "
										+ socket);
								break;
								
							case APPLET_CLASS_REQUESTER: //TODO ... finish
								new RequesterThread(socket,in, out).start();
								System.out.println("New class requester connection "
										+ socket);
								break;
								
							default:
								System.out.println("ERROR!");
							}
						}
					} catch (IOException e) {

						e.printStackTrace();
					} catch (ClassNotFoundException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			} catch (IOException e) {
				e.printStackTrace();
				try {
					out.close();
					in.close();
					serverSocket.close();
					return;
				} catch (IOException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
					return;
				}

			} finally {
				System.out.println("something wrong!!!");
				try {
					if (serverSocket != null)
						serverSocket.close();

				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	class RequesterThread extends Thread {

		private ObjectOutputStream out;
		private ObjectInputStream in;
		private Socket socket;

		public RequesterThread(Socket socket,ObjectInputStream in, ObjectOutputStream out) {
			super();
			this.out = out;
			this.in = in;
			this.socket=socket;

			System.out.println("New Class Requester");
		}

		@Override
		public void run() {
			while (true) {
				try {
					Result neededClassRequest = (Result) in.readObject();
					byte[] neededClass;
					if (neededClassRequest instanceof ClassRequest) {
						ClassRequest request = ((ClassRequest) neededClassRequest);
						System.out.println("Got request for: "
								+ request.getName());

						if (classes.containsKey(request)) {
							neededClass = classes.get(request);
						} else {
							System.out.println(request.getName() + ": "
									+ request.getProblemNumber() + ","
									+ request.getVersion());

							ClientInfo clientInfo = classSolver.getClientInfo(
									request.getProblemNumber(),
									request.getVersion());
							System.out.println();
							neededClass = clientInfo.getClass(request);
							classes.put(request, neededClass);
						}
						out.writeObject(neededClass);
						System.out.println("sent class " + neededClass);
					}
				}catch (EOFException e) {
					try {
						in.close();
						out.close();
						socket.close();
						return;
					} catch (IOException e1) {
						// TODO Auto-generated catch block
						e1.printStackTrace();
					}

				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();

					try {
						in.close();
						out.close();
						socket.close();
						return;
					} catch (IOException e1) {
						// TODO Auto-generated catch block
						e1.printStackTrace();
					}

				} catch (ClassNotFoundException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}

	}

}
